Ardupilot移植经验分享(1) | 您所在的位置:网站首页 › waf编译 wfatal error › Ardupilot移植经验分享(1) |
目录
前言背景为什么写这篇文章移植Ardupilot的方法有两种底层适配提取应用层代码两种方法对比
准备阅读源码阅读官方开发者wiki选择编译平台下载编译源代码切换版本的正确方式编译老版本手动下载编译器修改PATH配置配置ubuntu以支持运行32位程序老版本的编译命令
下载太慢或者失败
建立源码阅读环境使用Eclipse全貌跳转到定义选中符号高亮显示显示函数调用栈快速导航快速重命名记不住快捷键?
创建Eclipse工程需要获取哪些信息如何获取工程配置信息自动化创建工程
前言
笔者写这篇文章的重点是分享移植方法。Ardupilot代码在不断的演化更新,一篇非常细致的讲解移植步骤的文章总会过时,只有掌握了方法才能随机应变。必要的移植步骤是有的,不过在此之前,笔者会讲解一些辅助性的内容,比如搭建高效阅读源码的环境,移植Ardupilot会遇到哪些困难。 如果你对Ardupilot很好奇,想深入研究一些东西,但是又畏惧其复杂庞大的代码工程,那么笔者强烈推荐你耐心阅读本文,相信一定会对你有所帮助。 背景两年前接了个小项目,使用TI公司的单片机做飞控。这是学校的实验项目,主要是演示、推广之类的用途,要求不高。接触Ardupilot数年矣,一直想搞点大动作,正好借着这次机会,来一次真正的移植。 硬件平台为TM4C1294XL LaunchPad,软件平台为RT-Thread操作系统,基于Copter-3.5.7版本进行移植。 在10年前,四轴飞行器还是个冷门的东西,玩的人并不多,而接触过飞控开发的人就更少了。2013年全国大学生电子设计竞赛首次引入飞控类的赛题,越来越多的学生接触了飞控代码。许多开源飞控如雨后春笋般诞生,飞控开发的难度和门槛逐渐降低。 然而,历史悠久的Ardupilot的开发门槛却并未降低,个人感觉反而更高了。 学习成本高,导致Ardupilot令人望而生畏。笔者在决定移植Ardupilot之前,对其已有一定的了解,数年前还做过一次小移植(后面会细说)。纵使如此,也并无百分百的把握,是强烈的兴趣驱使我接下了这个项目。断断续续做了好几个月,经历了许多困难,中途都后悔了哈哈。 虽然困难,但Ardupilot是值得研究的。其包含的诸多功能,不仅仅可以用于飞行器。实际上,Ardupilot项目也不只有飞行器,其支持四种载具: Copter(旋翼机)Plane(固定翼)Rover(小车)Sub(潜艇)随便挑个功能,下图是Ardupilot支持的无源导航(即无GPS信号时)方式,有3D摄像头,光流,UWB定位,外置摄像头捕捉等等。 网上关于Ardupilot的代码分析和移植的文章有很多,笔者这篇文章的重点是分享方法。笔者移植的是Copter-3.5.7,而现在最新版本是Copter-4.0.3,代码肯定发生了很多变化。细节是死的,方法是活的。掌握了方法,才能灵活应变。 分析Ardupilot代码框架也是必不可少的,不过笔者不会讲解每一处需要修改的代码,比如关于外设驱动移植,会挑一些有代表性的来讲解。除此之外,笔者会详细地分享一些有助于成功移植的方法,比如如何加速源码的下载,命令行编译,搭建高效阅读源码的环境。 移植Ardupilot的方法有两种Ardupilot的代码框架 Ardupilot代码分为5个部分: 载具代码:APMrover2, ArduCopter, AntennaTracker, ArduPlane, ArduSub。对于飞行器项目来说就是飞控代码,是业务层的代码,比如飞行器初始化,飞行模式控制。共享库:libraries中除以AP_HAL开头的所有子目录。包含传感器驱动,姿态位置估算与控制。硬件抽象层(HAL):libraries中以AP_HAL开头子目录,如AP_HAL, AP_HAL_PX4, AP_HAL_Linux。工具目录:Tools。外部支持代码:modules。具体硬件平台的代码,如操作系统,中间件。要重点讲的是HAL。libraries/ AP_HAL中有一个顶级AP_HAL,用于定义其余代码与特定板功能之间的接口,然后每种硬件平台都有一个AP_HAL_XXX子目录,例如,用于基于AVR的板的AP_HAL_AVR,用于Pixhawk板的AP_HAL_PX4和用于基于Linux板的AP_HAL_Linux。 至于AP_HAL中的接口具体是什么样子的,在之后讲驱动代码时再说。这里介绍Ardupilot的代码构架的目的是引出移植的两个方法。 每种硬件平台都有一个AP_HAL_XXX子目录,而我们要移植到新平台的话,就是添加一个AP_HAL_XXX,比如AP_HAL_TI,并且实现所有HAL接口。这就是第一种方法,从底层适配。 与之相对应的是提取应用层代码,将飞控业务和算法代码提取出来,并做适当的修改,以适配自己的硬件。 下面详细分析下两种方法的特点和优劣。 底层适配底层适配,即实现所有HAL接口。libraries/AP_HAL目录中的文件如下: 下图最左边的分支,就是笔者TI板子,使用RT-Thread操作系统来提供AP_HAL所需要的线程等功能。
还可以更简单些,只提取姿态解算与控制,不管Ardupilot用了什么传感器,而是使用自己的传感器与电机驱动(比如pixhawk板子上使用的是L3GD20H和LSM303D,而你自己用mpu9250),可以实现最基础的飞行功能。笔者所说的数年前的小移植,就是这么干的,当时移植的是ArduCopter-2.9.1b。下图是移植出的姿态解算与控制代码的工程,提取并且转换成了C语言代码。 原始代码 // Main loop - 100hz static void fast_loop() { // IMU DCM Algorithm // -------------------- read_AHRS(); // reads all of the necessary trig functions for cameras, throttle, etc. // -------------------------------------------------------------------- update_trig(); // run low level rate controllers that only require IMU data run_rate_controllers(); // write out the servo PWM values // ------------------------------ set_servos_4(); // Inertial Nav // -------------------- read_inertia(); // optical flow // -------------------- #if OPTFLOW == ENABLED if(g.optflow_enabled) { update_optical_flow(); } #endif // OPTFLOW == ENABLED // Read radio and 3-position switch on radio // ----------------------------------------- read_radio(); read_control_switch(); // custom code/exceptions for flight modes // --------------------------------------- update_yaw_mode(); update_roll_pitch_mode(); // update targets to rate controllers update_rate_contoller_targets(); // agmatthews - USERHOOKS #ifdef USERHOOK_FASTLOOP USERHOOK_FASTLOOP #endif }移植后 void arducopter_fast_loop(void) { // IMU DCM Algorithm // -------------------- read_AHRS(); // custom code/exceptions for flight modes // --------------------------------------- update_yaw_mode(); update_roll_pitch_mode(); // update targets to rate controllers update_rate_contoller_targets(); //control the motor run_rate_controllers(); set_servos_4(); }移植后的arducopter_fast_loop分为如下几个部分: read_AHRS:从传感器读取角速度与加速度,使用DCM(方向余弦矩阵)计算姿态角。update_yaw_mode和update_roll_pitch_mode:根据飞行模式,当前姿态,目标姿态等来计算目标角速度(大地坐标系)。update_rate_contoller_targets:目标角速度转换(大地坐标系–>机体坐标系)。run_rate_controllers:根据目标角速度计算并输出电机信号。可以看出,这实现的是非常基础的飞行功能。其实笔者这里讲述几个函数的作用,并不是真想展开来讲飞控算法,而仅仅是想让大家体会一下:这种移植需要对飞控算法进行深入的了解。 两种方法对比底层适配需要深入了解HAL框架,了解每个HAL接口的功能并且实现它。这种方法,基本不用在应用层做改动,好处是:不需要深入研究应用层的代码,比如各种飞行模式,姿态解算与控制算法。再说直白点,就算你对飞控算法一无所知,只要你有扎实的嵌入式软件功底,就能完成移植。什么,软件功底也不是很厚实?不用担心,这正是我写这篇文章的目的啊。 相反,提取应用层代码则需要对飞控算法有更多的了解,并且需要深入分析Ardupilot的应用层代码,提取出你所需要的模块。好处也是与底层适配相反的,就是不需要了解HAL啦。你可以随意的修改应用层代码,包括调用驱动的地方。 应该选择哪一种,要看应用场景。 如果你只是想使用Ardupilot的一个小模块,比如姿态解算。Ardupilot将陀螺仪,加速度计,磁力计,GPS的数据进行融合,从而计算出稳定准确的姿态数据。把这些代码提取出来,应用到其他的项目中,是不错的想法。 如果你是想做一款飞行器,并且想使用到Ardupilot的诸多功能,如各种各样的飞行模式(定点悬停,路径规划),使用UWB进行无源导航,物体追踪,那就不宜提取应用层代码,而是做底层适配。否则,你可能要提取太多的代码,做太多的改动。 准备阅读源码在开始移植之前,有两个必要的步骤: 熟练使用一款Ardupilot飞控,比如Pixhawk。搭建源码的阅读、编译、调试环境。熟练的意思是,会配置、校准飞控,装到四轴上,能操控它进行基础的飞行。 虽然我们的最终目标是将飞控代码移植到新的平台上,不过搭建参考平台的源码环境非常重要。得先在这个参考平台上把源码读懂,当遇到疑问时可稍微修改下源码并调试验证。笔者的参考平台是第一代Pixhawk,如下图: 官方开发者wiki是最应该仔细阅读的资料,建议先阅读下面两个章节,其他的章节可根据需求选读。Building这章,只看自己使用的平台即可,比如Linux/Ubuntu。 Building the codeLearning the code 选择编译平台ardupilot支持以下平台,详见Building the code: Linux/UbuntuWindows(Cygwin或WSL)MacOSX笔者当初移植时,是在VMWare虚拟机中跑Ubuntu。在虚拟机中跑系统,最大的问题就是卡顿。这次写文章,笔者试了下WSL。WSL的全称是Windows10 Subsystem for Linux,就是在Windows10下直接跑Linux,不需要虚拟机。Setting up the Build Environment on Windows10 using WSL1 or WSL2推荐使用WSL2,不过笔者的Windows10版本不支持,因此使用了WSL1。 笔者使用的WSL1,也是跑ubuntu,大部分操作没有区别,有区别时会说明。WSL1的安装,可以参考Win10安装Ubuntu子系统及图形化界面详细教程。 下载编译源代码关于下载编译的步骤,Setting up the Build Environment (Linux/Ubuntu)说得非常清楚,ardupilot工程里面还包含了安装编译环境的脚本(Tools/environment_install/install-prereqs-ubuntu.sh,仅限于在Debian/Ubuntu系统中使用),笔者整理如下: 友情提示,这是下载并编译官方最新的源码,而并非是笔者移植所基于的3.5.7版本源码。 下载源码 git clone https://github.com/ArduPilot/ardupilot.git cd ardupilot git submodule update --init --recursive安装编译环境 # 当前目录为工程根目录,即ardupilot Tools/environment_install/install-prereqs-ubuntu.sh -y重启终端(wsl)或者重新登录(ubuntu),或者执行 . ~/.profile编译 # Ardupilot使用了Waf来管理项目编译。Waf是类似于make的项目构建系统,有Linux开发经验的同学肯定对make很熟悉。 ./waf configure --board Pixhawk1 ./waf copter下载源码、安装编译环境和执行编译,总共10来行命令。大家实际操作时,可能会遇到几个问题。 切换版本的正确方式 git clone https://github.com/ArduPilot/ardupilot.git上述命令下载的源码,默认处在master分支,若想切换到指定版本,需要使用git checkout来切换。比如切换到3.5.7版本并拉出一个新的分支,以便在其上做修改并且提交。 git checkout -b Copter-3.5-test Copter-3.5.7 # 切换了分支后,需要同步对子模块的引用。 git submodule update --init --recursiveardupilot不仅使用git来管理自己的源码,还使用git的submodule功能来跟踪其所依赖的子模块。对于不熟悉git的同学,切不可在github下载源码的压缩包,这样无法下载其子模块,也无法进行编译。 还有一点要注意,之前提到的install-prereqs-ubuntu.sh,在3.5.7版本时是不存在的。可以在master分支上运行它来安装环境,安装完后再切换分支。 编译老版本Tools/environment_install/install-prereqs-ubuntu.sh是官方为Debian/Ubuntu系统提供的一键安装编译调试环境的脚本,有兴趣的同学可以阅读下这个脚本,其内容非常简单:下载一些python库和编译器,下载的编译器为gcc-arm-none-eabi-6-2017-q2-update,位于/opt目录中。 用其编译最新源码是没问题的,若编译老版本代码,比如3.5.7,则会报错,比如笔者遇到的错误: /opt/gcc-arm-none-eabi-6-2017-q2-update/arm-none-eabi/include/c++/6.3.1/bits/basic_string.h: In function 'float std::__cxx11::stof(const string&, size_t*)': /opt/gcc-arm-none-eabi-6-2017-q2-update/arm-none-eabi/include/c++/6.3.1/bits/basic_string.h:5454:31: error: 'strtof' is not a member of 'std' { return __gnu_cxx::__stoa(&std::strtof, "stof", __str.c_str(), __idx); } ^~~ compilation terminated due to -Wfatal-errors. make[3]: *** [src/platforms/nuttx/CMakeFiles/platforms__nuttx.dir/px4_nuttx_impl.cpp.obj] Error 1 make[2]: *** [src/platforms/nuttx/CMakeFiles/platforms__nuttx.dir/all] Error 2 make[1]: *** [src/firmware/nuttx/CMakeFiles/build_firmware_px4fmu-v2.dir/rule] Error 2 make: *** [build_firmware_px4fmu-v2] Error 2需要替换成老的编译器,有如下几步: 手动下载编译器。执行Tools/environment_install/install-prereqs-ubuntu.sh。修改PATH配置:将PATH变量中的编译器路径改为老版本编译器的路径。配置ubuntu以支持运行32位程序。切换到目标分支,编译 手动下载编译器下载老版本编译器gcc-arm-none-eabi-4_9-2015q3,密码vyqb。 这链接还包含了最新的编译器,这将两个编译器都安装在/opt目录。之后执行install-prereqs-ubuntu.sh时,其检测到新编译器已存在,就不会再去官方网站下载(从官网下载速度极慢)。 以gcc-arm-none-eabi-4_9-2015q3-20150921-linux.tar.bz2为例,将其放至/opt目录下,并执行: cd /opt sudo tar xvf gcc-arm-none-eabi-4_9-2015q3-20150921-linux.tar.bz2 sudo rm gcc-arm-none-eabi-4_9-2015q3-20150921-linux.tar.bz2 修改PATH配置修改~/.profile,在最下面找到PATH变量,将gcc-arm-none-eabi-6-2017-q2-update改为gcc-arm-none-eabi-4_9-2015q3。 新编译器是64位的,老的是32位。默认情况下,64位ubuntu不支持运行32位程序,会报如下错误: $ arm-none-eabi-gcc -bash: /opt/gcc-arm-none-eabi-4_9-2015q3/bin/arm-none-eabi-gcc: cannot execute binary file: Exec format error若是跑ubuntu,则执行如下操作即可: sudo dpkg --add-architecture i386 sudo apt update sudo apt install libc6:i386 libncurses5:i386 libstdc++6:i386若是跑WSL(据说WSL2是没问题的,与ubuntu的操作一致),由于其内核就不支持32位程序,只能曲线救国,使用qemu来运行编译器: sudo apt install qemu-user-static sudo update-binfmts --install i386 /usr/bin/qemu-i386-static --magic '\x7fELF\x01\x01\x01\x03\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x03\x00\x01\x00\x00\x00' --mask '\xff\xff\xff\xff\xff\xff\xff\xfc\xff\xff\xff\xff\xff\xff\xff\xff\xf8\xff\xff\xff\xff\xff\xff\xff' sudo service binfmt-support start使用qemu的缺点是,编译速度比虚拟机还慢。。。。。。也许确实该用WSL2,笔者这次算是跳坑里了。 现在就可以运行老编译器了,no input files是因为我们没传源码文件路径,这个不用管。 $ arm-none-eabi-gcc arm-none-eabi-gcc: fatal error: no input files compilation terminated.如果关闭了所有WSL窗口,貌似WSL就关机了。再次启动WSL时,需要再次执行: sudo service binfmt-support start 老版本的编译命令之前的编译命令第一步如下,这是在配置硬件类型。 ./waf configure --board Pixhawk1笔者在编译3.5.7版本时,选择的是px4-v2。其实Pixhawk1和px4-v2对应的都是第1代Pixhawk,是一样的硬件,不同的是底层的软件平台。 px4-v2:基于PX4Firmware和Nuttx,这不是Ardupilot开发的,而是Pixhawk硬件厂商开发。Pixhawk1:Ardupilot团队使用ChibiOS重新开发了底层软件。笔者的编译命令: ./waf configure --board px4-v2 ./waf copter 下载太慢或者失败本节原来是放在此处写的,结果发现内容较多,导致篇幅太大,故后续单独出一篇文章:Ardupilot移植经验分享(1)–加速下载ardupilot工程。 建立源码阅读环境多年前,一个学弟研究Ardupilot的方法让我印象深刻,他将整个工程代码打印在纸上以进行阅读。 那时使用的是Ardunio版飞控,apm2.5,用Ardunio官方IDE来编译固件。IDE长这个样子: 其实Ardupilot开发团队肯定也不是用Ardunio IDE来开发,顶多是当成编译工具,肯定各有各自熟悉的开发利器,Editors and IDEs推荐了一些IDE。 笔者使用Eclipse阅读ardupilot源码,之后移植时用的是TI公司的Code Composer Studio,它也是基于Eclipse开发的。如果你并不打算使用Eclipse,“创建Eclipse工程”一节也值得一看,笔者将展示如何创建出与编译系统匹配的IDE工程,此方法是通用的,并不仅限于Eclipse。 使用Eclipse笔者之所以钟爱Eclipse,是因为它的代码编译和浏览功能很强大,可以极大的提高开发效率,也有助于高效地阅读一个全新的并且庞大的工程。 简单介绍几个功能。 全貌 左侧是项目浏览器视图,主要提供文件列表功能。右侧是大纲视图,显示当前文件中的符号表。中间是代码编辑器视图。多种方法触发: Ctrl+鼠标左击F3通过右键菜单当前光标在current_loc上(截屏捕捉到光标),所有的current_loc都高亮显示了。此功能有助于阅读复杂流程时关注某个变量或函数的访问情况。 要想知道上图中的read_inertia函数被谁调用了。 右键菜单Ctrl+Alt+H
可使用大纲视图在当前文件中进行导航,跳转到某个函数或变量。不过还有更快的方法,如果你记得目标符号的名称,或者记得一部分,都可以使用弹出式大纲(Ctrl+O)快速导航。 如果对某个函数或者变量的名称不满意,但是它被多处引用了。没关系,Shift+Alt+R就触发重构功能,只需要修改一处,其他地方自动更新。 刚才在介绍功能时,有的仅给出了快捷键的触发方式。不必担心,所有的功能都有相应的菜单。比如说各种导航功能,都可以通过右键菜单或者菜单栏中的“导航”找到。 创建Eclipse工程笔者还是以3.5.7版本为便进行讲解,方法是通用的。 选择IDE是一个问题,如何使用IDE来浏览ardupilot代码是另一个问题。Ardupilot工程不像大家平时用的Keil工程。如果你拿到一个可以编译的Keil工程,那么其所用到的代码自然就在工程配置之中。而Ardupilot使用Waf来编译,当我们选择了一个IDE后,需要自己来创建对应的工程。 笔者分享自己创建Eclipse工程的方法,从Waf提供的编译信息中提取相关信息以构建Eclipse工程,而且这是由代码自动完成。 为了方便说明,再次将ardupilot框架图放出来: 我们的目标是创建一个工程,并将这些代码包含在工程的文件列表之类。ArduCopter和libraries目录中的所有文件并不都是需要的,有的并没有参与编译,所以我们需要知道哪些文件参与了编译。不仅如此,还需要获取到编译时用到的: 宏定义头文件路径如下函数的行为受FRAME_CONFIG(机架类型配置)所控制,FRAME_CONFIG选择某一分支的代码参与编译。Eclipse有一项非常好用的功能,那就是如下图所示,其将宏未选中的代码灰化,使得用户可专注于选中的代码。 从上节我们明确了目标,需要如下信息: 代码文件列表宏定义头文件路径不仅我们需要这些信息,编译器更需要。ardupilot工程使用Waf来管理构建任务,所以,这些信息都在Waf的相关脚本文件之中,由其在调用编译器时进行传递。 不过笔者不了解Waf,也仅仅是在ardupilot中使用Waf,所以并不想花费额外的时间去深入了解它。笔者有一条以不变应万变的捷径,那就是从编译指令中提取。 gcc -I. -Ilibraries -DHELLO library.cpp -c -o library.o gcc -I. -Ilibraries -DHELLO main.cpp -c -o main.o gcc library.o main.o -o hello_world上面是我随便写2条编译命令和1条链接命令,对于单片机开发者来说,若无linux开发经验,可能不太熟悉这些指令。笔者简单介绍下几个参数的意义: -I添加头文件路径,这个路径+include中的路径构成完整的路径。比如前面那个drv_hrt.h的路径是由/mnt/g/ardupilot/src/ardupilot/build/px4-v2/modules/PX4Firmware/src + rivers/drv_hrt.h。.表示当前路径。-D添加宏定义。ardupilot的指令远比上面要复杂,参数比上面要多,但是格式是一样的。只要我们想办法得到ardupilot的编译指令,那就可获得我们所需要的信息。 笔者在创建linux内核,RT-Thread之类的复杂系统的IDE工程时,是通过在命令中加入verbose之类的参数,从而让构建系统打印出详细的编译日志,从而提取相关信息。下图是RT-Thread的编译: 而ardupilot直接提供了相关文件,这是笔者在默认级别的日志中发现的: 浓缩一下: { "directory": "/mnt/g/ardupilot/src/ardupilot/build/px4-v2", "file": "../../libraries/AP_NavEKF2/AP_NavEKF2_MagFusion.cpp", "command": "此处省略1千字" } directory:编译时的工作目录file:代码文件路径,是相对于directory的路径。command:编译指令,包含了头文件路径和宏定义的信息。其实command和这条编译main.cpp的指令是一样的,只不过它有更多的宏定义和头文件路径参数,另外,还有些别的用于控制编译行为的参数。 gcc -I. -Ilibraries -DHELLO main.cpp -c -o main.o笔者来拆分下这条超长的command指令给大家看。 编译器 /opt/gcc-arm-none-eabi-4_9-2015q3/bin/arm-none-eabi-g++宏定义 -DCONFIG_ARCH_BOARD_PX4FMU_V2 -D__STDC_FORMAT_MACROS -D__PX4_NUTTX -D__DF_NUTTX -DCONFIG_WCHAR_BUILTIN -D__CUSTOM_FILE_IO__ -DCONFIG_HAL_BOARD=HAL_BOARD_PX4 -DHAVE_OCLOEXEC=0 -DHAVE_STD_NULLPTR_T=0 -DUAVCAN_CPP_VERSION=UAVCAN_CPP03 -DUAVCAN_NO_ASSERTIONS=1 -DUAVCAN_NULLPTR=nullptr '-DSKETCHBOOK=\"/mnt/g/ardupilot/src/ardupilot\"'头文件路径 -Include visibility.h -Include ap_config.h -Ilibraries -Ilibraries/GCS_MAVLink -Imodules/uavcan/libuavcan/include/dsdlc_generated -I. -I../../libraries -I../../libraries/AP_Common/missing -I../../modules/uavcan/libuavcan/include -I../../modules/PX4Firmware/src -Imodules/PX4Firmware -Imodules/PX4Firmware/src -I../../modules/PX4Firmware/src/modules -I../../modules/PX4Firmware/src/include -I../../modules/PX4Firmware/src/lib -I../../modules/PX4Firmware/src/platforms -I../../modules/PX4Firmware/src/drivers/boards/px4fmu-v2 -Imodules/PX4Firmware/src/modules/px4_messages -Imodules/PX4Firmware/src/modules -I../../modules/PX4Firmware/mavlink/include/mavlink -I../../modules/PX4Firmware/src/lib/DriverFramework/framework/include -Isrc/lib/matrix -I../../src/lib/matrix -Imodules/PX4Firmware/px4fmu-v2/NuttX/nuttx-export/include -Imodules/PX4Firmware/px4fmu-v2/NuttX/nuttx-export/include/cxx -Imodules/PX4Firmware/px4fmu-v2/NuttX/nuttx-export/arch/chip -Imodules/PX4Firmware/px4fmu-v2/NuttX/nuttx-export/arch/common其他的编译参数,这在移植时可能用到,不过目前,我们无需关心。 -g -fno-exceptions -fno-rtti -std=gnu++0x -fno-threadsafe-statics -Wall -Werror -Wextra -Wno-sign-compare -Wfloat-equal -Wpointer-arith -Wmissing-declarations -Wno-unused-parameter -Werror=format-security -Werror=array-bounds -Wfatal-errors -Werror=unused-variable -Werror=reorder -Werror=uninitialized -Werror=init-self -Wframe-larger-than=1024 -Werror=unused-but-set-variable -Wformat=1 -Wdouble-promotion -Werror=double-promotion -Wno-missing-field-initializers -Os -fno-strict-aliasing -fomit-frame-pointer -funsafe-math-optimizations -ffunction-sections -fdata-sections -fno-strength-reduce -fno-builtin-printf -fvisibility=hidden -mcpu=cortex-m4 -mthumb -march=armv7e-m -mfpu=fpv4-sp-d16 -mfloat-abi=hard -nodefaultlibs -nostdlib -std=gnu++11 -fdata-sections -ffunction-sections -fno-exceptions -fsigned-char -Wall -Wextra -Wformat -Wshadow -Wpointer-arith -Wcast-align -Wundef -Wno-unused-parameter -Wno-missing-field-initializers -Wno-reorder -Wno-redundant-decls -Wno-unknown-pragmas -Werror=format-security -Werror=array-bounds -Werror=uninitialized -Werror=init-self -Werror=switch -Wfatal-errors -Werror=unused-but-set-variable -Wno-error=cast-align -Wlogical-op -Wframe-larger-than=1300 -fsingle-precision-constant -Wno-error=double-promotion -Wno-error=missing-declarations -Wno-error=float-equal -Wno-error=undef -Wno-error=cpp代码文件 ../../libraries/AP_NavEKF2/AP_NavEKF2_MagFusion.cpp编译成.o -c -o/mnt/g/ardupilot/src/ardupilot/build/px4-v2/libraries/AP_NavEKF2/AP_NavEKF2_MagFusion.cpp.0.o拆分完后再看,是不是非常清晰了。宏定义,头文件路径,代码路径,都有了。当然,这只是一条编译指令。我们需要解析compile_commands.json中的所有几百条指令。 补充个注意事项,前面在说编译命令时,使用的是: ./waf copter其不仅编译出多旋翼(四轴)的固件,还会编译直升机的。这两个固件有一个不同的宏定义,即上文提到的FRAME_CONFIG。 ~/ardupilot/build/px4-v2/bin$ tree . ├── arducopter ├── arducopter-heli ├── arducopter-heli.px4 └── arducopter.px4 四轴:FRAME_CONFIG=MULTICOPTER_FRAME直升机:FRAME_CONFIG=HELI_FRAME笔者试的情况是,其先编译四轴,再编译直升机,最终的compile_commands.json中留的是直升机的编译指令。如果你需要四轴的,使用下面这条指令,仅编译四轴: ./waf --targets bin/arducopter 自动化创建工程由于文件众多,一个个去手工解析是不现实的。我们可以写个程序来自动提取这些信息,笔者使用Java来解析: 其实这几百条指令的宏定义和头文件路径基本是一致,笔者解析时得出他们的并集,并且打印出每条重复的次数(在方括号之中)。 头文件路径如下,大部分的重复次数是429,这正是代码文件的个数,即它们在每条编译指令中都有。不过也有一些重复次数小于429的,最少的次数是81。这说明了自动化解析的重要性。如果仅手工解析第一条编译指令,那就会漏掉一些在后续编译指令中才出现的条目。 [0429] /mnt/g/ardupilot/src/ardupilot/modules/PX4Firmware/src [0348] /mnt/g/ardupilot/src/ardupilot/build/px4-v2/src/lib/matrix [0429] /mnt/g/ardupilot/src/ardupilot/modules/PX4Firmware/src/lib [0429] /mnt/g/ardupilot/src/ardupilot/build/px4-v2/modules/PX4Firmware/src/modules/px4_messages [0429] /mnt/g/ardupilot/src/ardupilot/modules/PX4Firmware/src/drivers/boards/px4fmu-v2 [0081] /mnt/g/ardupilot/src/ardupilot/ArduCopter/src/lib/matrix [0429] /mnt/g/ardupilot/src/ardupilot/build/px4-v2/modules/PX4Firmware/px4fmu-v2/NuttX/nuttx-export/include/cxx [0429] /mnt/g/ardupilot/src/ardupilot/build/px4-v2/modules/PX4Firmware/px4fmu-v2/NuttX/nuttx-export/include [0429] /mnt/g/ardupilot/src/ardupilot/build/px4-v2/libraries [0429] /mnt/g/ardupilot/src/ardupilot/build/px4-v2/modules/PX4Firmware/src/modules [0429] /mnt/g/ardupilot/src/ardupilot/modules/PX4Firmware/mavlink/include/mavlink [0429] /mnt/g/ardupilot/src/ardupilot/build/px4-v2/modules/PX4Firmware/px4fmu-v2/NuttX/nuttx-export/arch/common [0429] /mnt/g/ardupilot/src/ardupilot/modules/PX4Firmware/src/lib/DriverFramework/framework/include [0429] /mnt/g/ardupilot/src/ardupilot/modules/uavcan/libuavcan/include [0348] /mnt/g/ardupilot/src/ardupilot/src/lib/matrix [0429] /mnt/g/ardupilot/src/ardupilot/libraries/AP_Common/missing [0429] /mnt/g/ardupilot/src/ardupilot/build/px4-v2/. [0429] /mnt/g/ardupilot/src/ardupilot/build/px4-v2/modules/uavcan/libuavcan/include/dsdlc_generated [0429] /mnt/g/ardupilot/src/ardupilot/modules/PX4Firmware/src/platforms [0429] /mnt/g/ardupilot/src/ardupilot/libraries [0429] /mnt/g/ardupilot/src/ardupilot/build/px4-v2/modules/PX4Firmware [0429] /mnt/g/ardupilot/src/ardupilot/build/px4-v2/modules/PX4Firmware/px4fmu-v2/NuttX/nuttx-export/arch/chip [0429] /mnt/g/ardupilot/src/ardupilot/build/px4-v2/libraries/GCS_MAVLink [0429] /mnt/g/ardupilot/src/ardupilot/modules/PX4Firmware/src/modules [0429] /mnt/g/ardupilot/src/ardupilot/modules/PX4Firmware/src/include [0081] /mnt/g/ardupilot/src/ardupilot/build/px4-v2/ArduCopter/src/lib/matrix [0429] /mnt/g/ardupilot/src/ardupilot/build/px4-v2/modules/PX4Firmware/src宏定义的情况也一样,有少部分宏的重复次数小于429。 [0081] APM_BUILD_DIRECTORY=APM_BUILD_ArduCopter [0429] __PX4_NUTTX [0429] __CUSTOM_FILE_IO__ [0429] UAVCAN_NO_ASSERTIONS=1 [0429] __STDC_FORMAT_MACROS [0429] UAVCAN_NULLPTR=nullptr [0081] SKETCH="ArduCopter" [0429] CONFIG_WCHAR_BUILTIN [0081] SKETCHNAME="ArduCopter" [0429] HAVE_OCLOEXEC=0 [0429] __DF_NUTTX [0429] UAVCAN_CPP_VERSION=UAVCAN_CPP03 [0429] CONFIG_ARCH_BOARD_PX4FMU_V2 [0429] HAVE_STD_NULLPTR_T=0 [0065] FRAME_CONFIG=HELI_FRAME [0429] SKETCHBOOK="/mnt/g/ardupilot/src/ardupilot" [0429] CONFIG_HAL_BOARD=HAL_BOARD_PX4由于大部分都是重复的,所以它们的并集并没有多大。由程序解析出宏定义和头文件路径后,手工配置到Eclipse工程中是可行的。不过,别忘记,还有那几百个文件呢,如果手工把它们一个个加进工程,那是很费事儿的,搞不好还会遗漏些文件。 笔者不仅用程序进行解析,还使用程序来创建Eclipse工程,这就是所谓的自动化创建工程。 Eclipse的工程文件是以XML文本的形式存储,Keil也是如此。只要我们了解下其文本的格式,自动创建工程并非难事。 我们先看下Eclipse工程文件中宏定义、头文件路径和代码路径中格式。Eclipse工程目录下有两个文件,.project和.cproject。 .cproject存储配置,包含了宏定义和头文件路径。.project存储代码文件路径。宏定义 头文件路径 代码路径 笔者原本打算详细介绍自动化创建工程的步骤,不过突然发现这并不是一件简单的事情。需要准备更多的素材,需要更大的篇幅才能讲清楚讲透。因此就不在这里展开了,毕竟本文的宗旨是讲方法。如果大家感兴趣的话,笔者后续会专门出一篇文章来讲自动化创建工程。当然,ardupilot移植分享系列文件也不是仅此一篇,笔者规划的是至少3遍文章,第2篇分析代码,第3篇讲移植的细节。在这里向大家求一波三连支持,大家的认可是我创作的动力。 对了,项目源码,请观众笔者的公众号,回复:ardupilot-ti,即可获取。 别急,方法还未讲完。 除了宏定义、头文件路径和代码路径,工程中还有许多其他的内容。我们并不需要去了解这所有的内容,也不需要凭空生成工程文件。笔者使得的方法是,先创建一个模版工程,具体的步骤是: 使用Eclipse创建空工程。使用文本编辑器打开工程文件,在宏定义、头文件路径和代码路径的位置上加上特殊的字符串标记,这就是模版工程。写程序自动创建工程时,先组装好宏定义、头文件路径和代码路径的内容,然后替换模版工程中对应的字符串标记即可。下图是.cproject文件中的三个标记,分别对应宏、头文件路径、额外的头文件。 这次真结束了,源码分析和移植,有待下回分解。 |
CopyRight 2018-2019 实验室设备网 版权所有 |